LeetCode(15): 每k个一组翻转链表

hard!

题目描述:

给出一个链表,每 个节点为一组进行翻转,并返回翻转后的链表。

是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 的整数倍,那么将最后剩余节点保持原有顺序。

示例 :

给定这个链表:1->2->3->4->5

当 = 2 时,应当返回: 2->1->4->3->5

当 = 3 时,应当返回: 3->2->1->4->5

说明 :

  • 你的算法只能使用常数的额外空间。
  • 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

解题思路:

这道题让我们以每k个为一组来翻转链表,实际上是把原链表分成若干小段,然后分别对其进行翻转,那么肯定总共需要两个函数,一个是用来分段的,一个是用来翻转的,我们就以题目中给的例子来看,对于给定链表1->2->3->4->5,一般在处理链表问题时,我们大多时候都会在开头再加一个dummy node,因为翻转链表时头结点可能会变化,为了记录当前最新的头结点的位置而引入dummy node,那么我们加入dummy node后的链表变为-1->1->2->3->4->5,如果k为3的话,我们的目标是将1,2,3翻转一下,那么我们需要一些指针,pre和next分别指向要翻转的链表的前后的位置,然后翻转后pre的位置更新到如下新的位置:

-1->1->2->3->4->5
 |           |
pre         next

-1->3->2->1->4->5
          |  |
         pre next

以此类推,只要next走过k个节点,就可以调用翻转函数来进行局部翻转了。

C++解法一:

 1 /**
 2  * Definition for singly-linked list.
 3  * struct ListNode {
 4  *     int val;
 5  *     ListNode *next;
 6  *     ListNode(int x) : val(x), next(NULL) {}
 7  * };
 8  */
 9 class Solution {
10 public:
11     ListNode *reverseKGroup(ListNode *head, int k) {
12         if (!head || k == 1) return head;
13         ListNode *dummy = new ListNode(-1);
14         ListNode *pre = dummy, *cur = head;
15         dummy->next = head;
16         int i = 0;
17         while (cur) {
18             ++i;
19             if (i % k == 0) {
20                 pre = reverseOneGroup(pre, cur->next);
21                 cur = pre->next;
22             } else {
23                 cur = cur->next;
24             }
25         }
26         return dummy->next;
27     }
28     ListNode *reverseOneGroup(ListNode *pre, ListNode *next) {
29         ListNode *last = pre->next;
30         ListNode *cur = last->next;
31         while(cur != next) {
32             last->next = cur->next;
33             cur->next = pre->next;
34             pre->next = cur;
35             cur = last->next;
36         }
37         return last;
38     }
39 };

也可以在一个函数中完成,我们首先遍历整个链表,统计出链表的长度,然后如果长度大于等于k,我们开始交换节点,当k=2时,每段我们只需要交换一次,当k=3时,每段需要交换两次,所以i从1开始循环,注意交换一段后更新pre指针,然后num自减k,直到num<k时循环结束。

C++解法二:

 1 class Solution {
 2 public:
 3     ListNode* reverseKGroup(ListNode* head, int k) {
 4         ListNode *dummy = new ListNode(-1), *pre = dummy, *cur = pre;
 5         dummy->next = head;
 6         int num = 0;
 7         while (cur = cur->next) ++num;
 8         while (num >= k) {
 9             cur = pre->next;
10             for (int i = 1; i < k; ++i) {
11                 ListNode *t = cur->next;
12                 cur->next = t->next;
13                 t->next = pre->next;
14                 pre->next = t;
15             }
16             pre = cur;
17             num -= k;
18         }
19         return dummy->next;
20     }
21 };

也可以使用递归来做,我们用head记录每段的开始位置,cur记录结束位置的下一个节点,然后我们调用reverse函数来将这段翻转,然后得到一个new_head,原来的head就变成了末尾,这时候后面接上递归调用下一段得到的新节点,返回new_head即可。

C++解法三:

 1 class Solution {
 2 public:
 3     ListNode* reverseKGroup(ListNode* head, int k) {
 4         ListNode *cur = head;
 5         for (int i = 0; i < k; ++i) {
 6             if (!cur) return head;
 7             cur = cur->next;
 8         }
 9         ListNode *new_head = reverse(head, cur);
10         head->next = reverseKGroup(cur, k);
11         return new_head;
12     }
13     ListNode* reverse(ListNode* head, ListNode* tail) {
14         ListNode *pre = tail;
15         while (head != tail) {
16             ListNode *t = head->next;
17             head->next = pre;
18             pre = head;
19             head = t;
20         }
21         return pre;
22     }
23 };

 

posted @ 2018-06-04 16:19  Ariel_一只猫的旅行  阅读(4784)  评论(0编辑  收藏  举报